package com.pb.infra.oth.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;
import org.junit.platform.commons.util.ReflectionUtils;
import org.springframework.http.HttpOutputMessage;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @auther: liaohaoyuan
 * @time: 2022/6/8 09:31
 * @description
 */
public class PdfUtli {
    private static BaseFont baseFont;
    private static Font titleFont;
    private static Font headerFont;
    private static Font bodyFont;

    static {
        try {
            baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
                    BaseFont.NOT_EMBEDDED);//创建字体
            titleFont = new Font(baseFont, 12);//使用字体
            headerFont = new Font(baseFont, 12, Font.BOLD);//使用字体
            headerFont.setColor(BaseColor.WHITE);
            bodyFont = new Font(baseFont, 10);//使用字体
        }catch (FileNotFoundException e) {
            e.printStackTrace();
        }  catch (IOException e) {
            e.printStackTrace();
        }catch (DocumentException e){
            e.printStackTrace();
        }
    }
    /**
     * 在已经生成的pdf上添加电子签章，生成新的pdf并将其输出出来
     * @param path  //临时文件路径
     * @param httpOutputMessage  HttpOutputMessage
     * @param reason    //签名的原因，显示在pdf签名属性中，随便填
     * @param location //签名的地点，显示在pdf签名属性中，随便填
     * @throws GeneralSecurityException
     * @throws IOException
     * @throws DocumentException
     */
    public void returnFlow(String path, HttpOutputMessage httpOutputMessage, String reason, String location){
        try {
            OutputStream body = httpOutputMessage.getBody();
            PdfUtli.sign(path,body,reason,location);
        }catch (IOException e){
            e.printStackTrace();
        }

    }

    /**
     * 在已经生成的pdf上添加电子签章，生成新的pdf并将其输出出来
     * @param path  //临时文件路径
     * @param response  HttpServletResponse
     * @param reason    //签名的原因，显示在pdf签名属性中，随便填
     * @param location //签名的地点，显示在pdf签名属性中，随便填
     * @throws GeneralSecurityException
     * @throws IOException
     * @throws DocumentException
     */
    public void returnFlow(String path, HttpServletResponse response, String reason, String location){
        try {
            OutputStream outputStream = response.getOutputStream();
            PdfUtli.sign(path,outputStream,reason,location);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 在已经生成的pdf上添加电子签章，生成新的pdf并将其输出出来
     * @param path  //临时文件路径
     * @param dest   返回流
     * @param reason    //签名的原因，显示在pdf签名属性中，随便填
     * @param location //签名的地点，显示在pdf签名属性中，随便填
     * @throws GeneralSecurityException
     * @throws IOException
     * @throws DocumentException
     */
    private static void sign(String path,OutputStream dest, String reason, String location) {
        InputStream src = null;
        InputStream p12Stream = null;
        PdfReader reader = null;
        try {
            src = new FileInputStream(path);
            //读取keystore ，获得私钥和证书链
            String basePath = System.getProperty("user.dir")+"\\primefmapp-boot\\src\\main\\resources\\static\\secretkey.p12";
            p12Stream = new FileInputStream(basePath);//获取p12 路径
            char[] password = {'L','h','y','@','8','C','2','Z','4','M'};
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(p12Stream, password);
            String alias = (String) ks.aliases().nextElement();
            PrivateKey pk = (PrivateKey) ks.getKey(alias, password);
            Certificate[] chain = ks.getCertificateChain(alias);

            //下边的步骤都是固定的，照着写就行了，没啥要解释的
            // Creating the reader and the stamper，开始pdfreader
            reader = new PdfReader(src);
            //目标文件输出流
            //创建签章工具PdfStamper ，最后一个boolean参数
            //false的话，pdf文件只允许被签名一次，多次签名，最后一次有效
            //true的话，pdf可以被追加签名，验签工具可以识别出每次签名之后文档是否被修改
            PdfStamper stamper = PdfStamper.createSignature(reader, dest, '\0', null, false);
            // 获取数字签章属性对象，设定数字签章的属性
            PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
            appearance.setReason(reason);
            appearance.setLocation(location);
            //设置签名的位置，页码，签名域名称，多次追加签名的时候，签名预名称不能一样
            //签名的位置，是图章相对于pdf页面的位置坐标，原点为pdf页面左下角
            //四个参数的分别是，图章左下角x，图章左下角y，图章右上角x，图章右上角y
            //appearance.setVisibleSignature(new Rectangle(300, 600, 630, 500), 1, "sig1");
            //读取图章图片，这个image是itext包的image
        /*Image image = Image.getInstance(chapterPath);
        appearance.setSignatureGraphic(image);*/
            appearance.setCertificationLevel(PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED);
            //设置图章的显示方式，如下选择的是只显示图章（还有其他的模式，可以图章和签名描述一同显示）
            appearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);

            // 这里的itext提供了2个用于签名的接口，可以自己实现，后边着重说这个实现
            // 摘要算法
            ExternalDigest digest = new BouncyCastleDigest();
            // 签名算法
            ExternalSignature signature = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, null);
            // 调用itext签名方法完成pdf签章CryptoStandard.CMS 签名方式，建议采用这种
            MakeSignature.signDetached(appearance, digest, signature, chain, null, null, null, 0, MakeSignature.CryptoStandard.CMS);

        }    catch (IOException e){
            e.printStackTrace();
        } catch (GeneralSecurityException e){
            e.printStackTrace();
        } catch (DocumentException e){
            e.printStackTrace();
        }finally {
            File file = new File(path);
            file.delete();
           try {
               if(src!=null){
                   src.close();
               }
               if(p12Stream != null){
                   p12Stream.close();
               }
               if (dest !=null){
                   dest.flush();
                   dest.close();
               }
               if (reader!=null){
                   reader.close();
               }
           }catch (IOException e){
               e.printStackTrace();
           }
        }
    }
    /**
     * 将泛型对象转化为Map对象
     * @param object
     * @return
     */
    public static Map entityToMap(Object object) {
        Map map = new HashMap();
        for (Field field : object.getClass().getDeclaredFields()){
            try {
                //中危
                //boolean flag = field.isAccessible();
                //field.setAccessible(true);

                //改成spring提供的util语法
                ReflectionUtils.makeAccessible(field);

                Object o = field.get(object);
                map.put(field.getName(), o);

                //field.setAccessible(flag);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    /**
     * 将Map转换为Map对象
     * @param j
     * @return
     */
    public static Map<String,Object> JsonToMap(JSONObject j)
    {
        Map<String,Object> Mapmap = new HashMap < > ();
        Iterator<String> iterator = j.keySet().iterator();
        while (iterator.hasNext())
        {
            String key = (String) iterator.next();
            Object value = j.get(key);
            Mapmap.put(key, value);
        }
        return Mapmap;
    }

    /**
     *
     * 创建普通表格
     * @param list 参数
     * @param header 列顺序
     * @param titles 表头
     */
    public PdfPTable createBody(List list, String[] header,String[] titles) {
        PdfPTable table = new PdfPTable(titles.length);

        table.setHorizontalAlignment(Element.ALIGN_CENTER);//设置表格居中
        table.setWidthPercentage(100);
        for (String title : titles) {
            PdfPCell pdfPCell = new PdfPCell(new Phrase(title,headerFont));
            // 设置单元格的垂直对齐方式 ： 居中
            pdfPCell.setVerticalAlignment(pdfPCell.ALIGN_MIDDLE);
            // 设置单元格的水平对齐方式 ：居中对齐
            pdfPCell.setHorizontalAlignment(Element.ALIGN_CENTER);
            pdfPCell.setMinimumHeight(16);//设置表格行高
            pdfPCell.setBackgroundColor(new BaseColor(37, 120, 181));// 设置背景颜色
            table.addCell(pdfPCell);
        }
        for (Object rowData : list) {
            Map map1 = JSON.parseObject(JSONArray.toJSONString(rowData), HashMap.class);
            for (int i = 0; i < header.length; i++) {
                Object o = map1.get(header[i]);
                String s ="";
                if (o!=null){
                    s=o.toString();
                }
                PdfPCell cell = new PdfPCell(new Phrase(s,bodyFont));
                table.addCell(cell);
            }
        }

        return table;
    }

    /**
     * 创建表头
     * @param obj 单元格内容
     * @param col 合并行数
     * @param row 合并列数
     */
   public PdfPCell createHeader(Object obj,Integer col,Integer row){
       PdfPCell pdfPCell = new PdfPCell(new Phrase(obj.toString(),headerFont));
       // 设置单元格的垂直对齐方式 ： 居中
       pdfPCell.setVerticalAlignment(pdfPCell.ALIGN_MIDDLE);
       // 设置单元格的水平对齐方式 ：居中对齐
       pdfPCell.setHorizontalAlignment(Element.ALIGN_CENTER);
       pdfPCell.setMinimumHeight(16);//设置表格行高
       pdfPCell.setBackgroundColor(new BaseColor(37, 120, 181));// 设置背景颜色
       if (col!=null&&col>0){
           pdfPCell.setColspan(col);
       }
       if (row!=null&&row>0){
           pdfPCell.setRowspan(row);
       }

       return pdfPCell;
   }

    /**
     * 创建表头
     * @param obj
     * @return
     */
    public PdfPCell createHeader(Object obj){
        PdfPCell pdfPCell = new PdfPCell(new Phrase(obj.toString(),headerFont));
        // 设置单元格的垂直对齐方式 ： 居中
        pdfPCell.setVerticalAlignment(pdfPCell.ALIGN_MIDDLE);
        // 设置单元格的水平对齐方式 ：居中对齐
        pdfPCell.setHorizontalAlignment(Element.ALIGN_CENTER);
        pdfPCell.setMinimumHeight(16);//设置表格行高
        pdfPCell.setBackgroundColor(new BaseColor(37, 120, 181));// 设置背景颜色
        return pdfPCell;
    }

    /**
     * 创建表格
     * @param num 表格列数
     * @return
     */
    public PdfPTable createTable(int num){
        PdfPTable pdfPTable = new PdfPTable(num);
        pdfPTable.setHorizontalAlignment(Element.ALIGN_CENTER);//设置表格居中
        pdfPTable.setWidthPercentage(100);
        return pdfPTable;
    }

    /**
     * 单个创建单元格
     * @param obj 单元格内容
     * @return
     */
    public  PdfPCell createCell(Object obj){
       return this.createCell(obj, 1, 1);
    }

    /**
     * 单个创建单元格
     * @param obj 单元格填充文字
     * @param col 合并行
     * @param row 合并列
     */
    public  PdfPCell createCell(Object obj,Integer col,Integer row){
       PdfPCell cell = new PdfPCell(new Phrase(obj.toString(),bodyFont));
       if (col!=null&&col>0){
           cell.setColspan(col);
       }
       if (row!=null&&row>0){
           cell.setRowspan(row);
       }
       return cell;
    }

    /**
     * 创建段落
     * @param str
     */
    public Paragraph createSection(String str){

        Paragraph p = new Paragraph(str,titleFont);
        p.setFirstLineIndent(28);
        p.setAlignment(Element.ALIGN_LEFT);
        p.setSpacingBefore(15f);
        return p;
    }

    /**
     * 创建标题
     * @param str 标题文字
     * @param direction 标题方向 - left 居左，right 居右，center 居中
     * @param size 字号
     * @param isblodFont 是否字体加粗
     */
    public Paragraph CreateTitle(String str,String direction,Integer size,Boolean isblodFont){
        Font titleFontStyle=null;//使用字体
        if (isblodFont){
            titleFontStyle = new Font(baseFont, size,Font.BOLD);//使用字体
        }else {
            titleFontStyle = new Font(baseFont, size);
        }
        Paragraph p = new Paragraph(str,titleFontStyle);
        try {

            if ("center".equals(direction)){
                p.setAlignment(Element.ALIGN_CENTER);
            }else if("right".equals(direction)){
                p.setAlignment(Element.ALIGN_RIGHT);
            }else {
                p.setAlignment(Element.ALIGN_LEFT);
            }
            p.setFirstLineIndent(28);
            p.setSpacingBefore(15f);
        }catch (Exception e){
            e.printStackTrace();
        }
        return p;
    }

    /**
     * 讲表格元素挂载到document元素上
     */
    public void mountElement(PDFBuilder builder,List<Element> list){
        Document document =null;
        PdfWriter pdfWriter = null;
        try {
            if (builder.getIslengthways()){
                    document = new Document(builder.getPageSize());
            }else {
                Rectangle pageSize = new RectangleReadOnly(builder.getPageSize().getHeight(), builder.getPageSize().getWidth());
                //new RectangleReadOnly(595,842);
                
                pageSize.rotate();
                document = new Document(pageSize);
            }
            
            pdfWriter = PdfWriter.getInstance(document, builder.getOut());
            pdfWriter.setPageEvent(new PdfHeaderFooter());
            document.open();
            document.addAuthor(builder.getAuthor());
            document.addTitle(builder.getTitle());
            for (Element element : list) {
                document.add(element);
            }
        }catch (DocumentException e){
            e.printStackTrace();
        }finally {
            if (document!=null){
                document.close();
            }
            if (pdfWriter!=null){
                pdfWriter.close();
            }
        }
    }
}
